package sizeof;

// ----------------------------------------------------------------------------
/**
 * A simple class to experiment with your JVM's garbage collector and memory
 * sizes for various data types.
 * 
 * @author <a href="mailto:vlad@trilogy.com">Vladimir Roubtsov</a>
 */
public class Sizeof {
	public static void main(String[] args) throws Exception {
		// "warm up" all classes/methods that we are going to use:
		runGC();
		getUsedMemory();
		final int count = 10000; // 10000 or so is enough for small ojects
		// array to keep strong references to allocated objects:
		Object[] objects = new Object[count];
		long heap1 = 0;
		// allocate count+1 objects, discard the first one:
		for (int i = -1; i < count; ++i) {
			Object object;
			// INSTANTIATE YOUR DATA HERE AND ASSIGN IT TO 'object':
			object = new Object(); // 8 bytes
			// object = new Integer (i); // 16 bytes
			// object = new Long (i); // same size as Integer?
			// object = createString (10); // 56 bytes? fine...
			// object = createString (9)+' '; // 72 bytes? the article explains
			// why
			// object = new char [10]; // 32 bytes
			// object = new byte [32][1]; // 656 bytes?!
			if (i >= 0)
				objects[i] = object;
			else {
				object = null; // discard the "warmup" object
				runGC();
				heap1 = getUsedMemory(); // take a "before" heap snapshot
			}
		}
		runGC();
		long heap2 = getUsedMemory(); // take an "after" heap snapshot:
		final int size = Math.round(((float) (heap2 - heap1)) / count);
		System.out.println("'before' heap: " + heap1 + ", 'after' heap: " + heap2);
		System.out.println("heap delta: " + (heap2 - heap1) + ", {" + objects[0].getClass() + "} size = " + size + " bytes");
	}

	// a helper method for creating Strings of desired length
	// and avoiding getting tricked by String interning:
	public static String createString(final int length) {
		final char[] result = new char[length];
		for (int i = 0; i < length; ++i)
			result[i] = (char) i;
		return new String(result);
	}

	// this is our way of requesting garbage collection to be run:
	// [how aggressive it is depends on the JVM to a large degree, but
	// it is almost always better than a single Runtime.gc() call]
	private static void runGC() throws Exception {
		// for whatever reason it helps to call Runtime.gc()
		// using several method calls:
		for (int r = 0; r < 4; ++r)
			runGC0();
	}

	private static void runGC0() throws Exception {
		long usedMem1 = getUsedMemory(), usedMem2 = Long.MAX_VALUE;
		for (int i = 0; (usedMem1 < usedMem2) && (i < 1000); ++i) {
			runtime.runFinalization();
			runtime.gc();
			// Thread.yield();
			usedMem2 = usedMem1;
			usedMem1 = getUsedMemory();
		}
	}

	private static long getUsedMemory() {
		return runtime.totalMemory() - runtime.freeMemory();
	}

	private static final Runtime runtime = Runtime.getRuntime();
}
